`ifndef NewLabel_SYSIF_AGENT_SV
`define NewLabel_SYSIF_AGENT_SV

import ovm_pkg::*;
`include "ovm_macros.svh"
import	definitions::*;

interface SysIf(input logic sclk, rst_n);

	wire logic [23:0]	data;
	logic [23:0]	data_wr;
	logic [4:0]		addr;
	logic cs;
	logic wr;
	logic wr_full_flag;
	logic rd_empty_flag;
	int data_id;

	assign data = wr? data_wr : 'z;

 	/*modport drv(
		input sclk, rst_n,
		inout data, 
		output addr, cs, wr, wr_full_flag, rd_empty_flag
  	);*/
 	   
endinterface

////////////////////////// items /////////////////////////
class tr_sysif extends ovm_sequence_item;
	rand logic[23:0] data;
	rand logic[4:0]	addr;
	rand Bus_OP_t op;
	rand int trans_delay;
	rand int data_id;
	rand bit last_data;

	constraint trans_delay_con{trans_delay>=0; trans_delay<=30;}

	`ovm_object_utils_begin(tr_sysif)
		`ovm_field_int(data, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(addr, OVM_ALL_ON + OVM_HEX)
		`ovm_field_enum(Bus_OP_t, op, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(trans_delay, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(data_id, OVM_ALL_ON + OVM_DEC)
		`ovm_field_int(last_data, OVM_ALL_ON + OVM_HEX)
	`ovm_object_utils_end

	function new (string name = "tr_sysif");
		super.new(name);
	endfunction : new

	virtual function void do_print(ovm_printer printer);
		super.do_print(printer);
	endfunction : do_print
	
endclass : tr_sysif

////////////////////////// drivers /////////////////////////
class NewLabel_sysif_driver extends ovm_driver #(tr_sysif);
	virtual SysIf if0;
	`ovm_component_utils(NewLabel_sysif_driver)

	function new(string name, ovm_component parent);
		super.new(name, parent);
	endfunction : new

	function void assign_vi(virtual interface SysIf if0);
	  this.if0 = if0;     
	endfunction : assign_vi

	task run();
		init_signals();

		forever begin
		  seq_item_port.get_next_item(req);
		  //$cast(rsp, req.clone());
		  //req.print();
			//ovm_report_info("TRP", $psprintf("Detail:\n%s", req.sprint()), OVM_FULL);

		  drive_transfer(req);
		        
		  $cast(rsp, req.clone());
		  rsp.set_id_info(req);
		  seq_item_port.item_done(rsp);
		end
	endtask : run

	task init_signals();
		time run_delay;
		assert(get_config_int("run_delay", run_delay));
		#run_delay;
		wait(if0.rst_n);
		//if0.data = 24'b0;
		if0.addr <= '0;
		if0.cs <= 1'b0;
		if0.wr <= 1'b0;
	endtask : init_signals

	task drive_transfer(tr_sysif trans);
		if(trans.trans_delay>0)
		begin
			#trans.trans_delay;    
		end
		
		if(trans.op == WRITE)
		begin
			//ovm_report_info("sysif config/write", "Driving...", OVM_HIGH);
		    //ovm_report_info("sysif config/write", $psprintf("Detail:\n%s", trans.sprint()), OVM_HIGH);
			if(trans.data_id % 10000 == 0)
		    	$display("%d", trans.data_id);

	    	//@(wr_full_flag === 1'b0);
		    @(posedge if0.sclk iff if0.wr_full_flag === 1'b0)
		    begin
		    	if0.cs <= #1 1'b1;
		    	if0.data_wr <= #1 trans.data;
	    		if0.data_id = trans.data_id;
		    	if0.addr <= #1 trans.addr;
	    		if0.wr <= #1 1'b1;
			end

			if(trans.addr[4] != 1'b1)
			begin
				repeat(3)
				@(posedge if0.sclk);
			end

			if(trans.last_data == 1'b1)
			begin
				@(posedge if0.sclk)
				begin
			    	//if0.data_wr <= #1 '0;
					if0.addr <= #1 '0;
					if0.cs <= #1 1'b0;
					if0.wr <= #1 1'b0;
				end
			end
		end
		else if(trans.op == READ)
		begin
			if(trans.data_id % 10000 == 0)
		    	$display("%d", trans.data_id);
		
			if(!(if0.rd_empty_flag===1'b1 && trans.data_id>0))
				wait(if0.rd_empty_flag===1'b0);
			@(posedge if0.sclk)
		    begin
		    	if0.cs <= #1 1'b1;
		    	//trans.data = if0.data;
		    	if0.addr <= #1 trans.addr;
	    		if0.wr <= #1 1'b0;
	    		if0.data_id <= trans.data_id;
			end
			if(trans.data_id == -100)
			begin
				repeat(1) @(posedge if0.sclk);
			end
			else
			begin
				trans.data = if0.data;
				//0.data_id = trans.data_id;
			end
			
			if(trans.addr[4] != 1'b1)
			begin
				repeat(3)
				@(posedge if0.sclk);
			end

			if(trans.last_data == 1'b1)
			begin
				@(posedge if0.sclk)
				begin
			    	//if0.data = '0;
					if0.addr <= #1 '0;
					if0.cs <= #1 1'b0;
					if0.wr <= #1 1'b0;
				end
			end
		end
	endtask : drive_transfer
	
endclass : NewLabel_sysif_driver

////////////////////////// sequencers /////////////////////////
class NewLabel_sysif_sequencer extends ovm_sequencer #(tr_sysif);
	`ovm_sequencer_utils(NewLabel_sysif_sequencer)

	function new(string name, ovm_component parent=null);
		super.new(name, parent);
		`ovm_update_sequence_lib_and_item(tr_sysif)
	endfunction

endclass : NewLabel_sysif_sequencer

////////////////////////// sequences /////////////////////////
virtual class NewLabel_sysif_base_sequence extends ovm_sequence #(tr_sysif);  
	function new(string name="NewLabel_sysif_base_seq");    
		super.new(name);  
	endfunction

	virtual task pre_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s pre_body() raising an ovm_test_done objection", 
					get_sequence_path()), OVM_MEDIUM);    
		ovm_test_done.raise_objection(this);  
	endtask  

	virtual task post_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s post_body() dropping an ovm_test_done objection",
					get_sequence_path()), OVM_MEDIUM);
		ovm_test_done.drop_objection(this);  
	endtask  
endclass : NewLabel_sysif_base_sequence

class sysif_config_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "sysif_config_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(sysif_config_seq, NewLabel_sysif_sequencer)

  tr_sysif lli;
  logic [11:0] img_width = 'd16;
  logic [11:0] img_height = 'd16;
  logic [2:0]	extract_mode = 'b001;
  logic [1:0]	label_mode = 'b01;
  logic aux_en = 1'b1;
  //logic [11:0]	aux_trigger_depth = 'hf;
  int aux_trigger_depth = 'h9;
  
  virtual task body();
    void'(p_sequencer.get_config_int("sysif_config_seq.aux_trigger_depth", aux_trigger_depth));
    void'(p_sequencer.get_config_int("sysif_config_seq.img_width", img_width));
    void'(p_sequencer.get_config_int("sysif_config_seq.img_height", img_height));
    void'(p_sequencer.get_config_int("sysif_config_seq.aux_en", aux_en));
    void'(p_sequencer.get_config_int("sysif_config_seq.extract_mode", extract_mode));
    void'(p_sequencer.get_config_int("sysif_config_seq.label_mode", label_mode));
    

	`ovm_create(lli);
	lli.addr = 5'h01;
	lli.data = {img_height, img_width};
	//lli.data = 24'b0000_0001_0000_0000_0001_0000;	//16*16
	//lli.data = 24'b0000_1000_0000_0000_1000_0000;	//128*128
	lli.op = WRITE;
	`ovm_send(lli);

	`ovm_create(lli);
	lli.addr = 5'h02;
	lli.data = {extract_mode, label_mode, aux_en, 18'b0};
	//lli.data = 24'b0010_1000_0000_0000_0000_0000;
	lli.op = WRITE;
	`ovm_send(lli);

	`ovm_create(lli);
	lli.addr = 5'h00;
	lli.data = {aux_trigger_depth, 11'b0, 1'b1};
	//lli.data = 24'b0000_0000_0000_0000_0000_0001;
	lli.op = WRITE;
	`ovm_send(lli);
  endtask : body
endclass : sysif_config_seq

class Special_16X16_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "Special_16X16_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(Special_16X16_seq, NewLabel_sysif_sequencer)

  tr_sysif lli;
  string filename = "./testImg/srcImage_16x16_30.txt";
  string filename2 = "./testImg/srcImage_16x16_30_result.txt";
  int width=16, height=16;
  logic[7:0] src[16][16];
  logic[23:0]	dst[16][16];
  //logic[7:0]	src[][];
  //logic[23:0]	dst[][];
  //src = new[height][width];
  //dst = new[height][width];
  int i,j,k=0;
  integer fileId;
  
  virtual task body();
    void'(p_sequencer.get_config_string("Special_16X16_seq.filename", filename));
    void'(p_sequencer.get_config_string("Special_16X16_seq.filename2", filename2));
    void'(p_sequencer.get_config_int("Special_16X16_seq.width", width));
    void'(p_sequencer.get_config_int("Special_16X16_seq.height", height));

    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename), OVM_MEDIUM);
    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename2), OVM_MEDIUM);

    $display("%d %d", width, height);

    /*src = new[height];
    foreach(src[i])
    begin
    	src[i] = new[width];
    end
    dst = new[height];
    foreach(dst[i])
    begin
    	dst[i] = new[width];
    end*/
    $readmemh(filename, src);
    //$readmemh("./testImg/srcImage_16x16_30.txt", src);

  	for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++, k++)
  		begin
			`ovm_create(lli)
			lli.data = src[i][j];
			lli.op = WRITE;
			lli.addr = 5'b11111;
			lli.data_id = k;
			if(i*j==15*15)
				lli.last_data = 1;
			`ovm_send(lli)
			get_response(rsp);
		end
	end
	`ovm_create(lli)
	lli.op = READ;
	lli.addr = 5'b11110;
	lli.data_id = -100;
	`ovm_send(lli)
	get_response(rsp);
	`ovm_info("sysif receive", "Init(1)...", OVM_LOW)

	foreach(dst[i,j])
	begin
		`ovm_create(lli)
		lli.op = READ;
		lli.addr = 5'b11110;
		lli.data_id = k;
		if(k==1)
			lli.last_data = 1;
		k--;
		`ovm_send(lli)
		get_response(rsp);
		`ovm_info("sysif receive", "Receiving...", OVM_LOW)
	    ovm_report_info("sysif receive", $psprintf("Detail:\n%s", rsp.sprint()), OVM_HIGH);
		dst[height-1-i][width-1-j] = rsp.data;
	end

	fileId = $fopen(filename2, "w");
	foreach(dst[i,j])
	begin
		$write("%d", dst[i][j]);
		$fwrite(fileId, "%d", dst[i][j]);
		//$fdisplay(fileId, "%d", dst[i][j]);
		if(j==15)
		begin
			$write("\n");
			$fwrite(fileId, "\n");
			//$fdisplay(fileId, "\n");
		end
	end
	$fclose(fileId);
  endtask : body
endclass : Special_16X16_seq

class Special_128X128_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "Special_128X128_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(Special_128X128_seq, NewLabel_sysif_sequencer)

  tr_sysif lli;
  string filename = "./testImg/srcImage_128x128_30.txt";
  string filename2 = "./testImg/srcImage_128x128_30_result.txt";
  int width=128, height=128;
  logic[7:0] src[128][128];
  logic[23:0]	dst[128][128];
  //src = new[height][width];
  //dst = new[height][width];
  int i,j,k=0;
  integer fileId;
  
  virtual task body();
	void'(p_sequencer.get_config_string("Special_128X128_seq.filename", filename));
    void'(p_sequencer.get_config_string("Special_128X128_seq.filename2", filename2));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.width", width));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.height", height));

    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename), OVM_MEDIUM);
    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename2), OVM_MEDIUM);

    $readmemh(filename, src);

  	for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++, k++)
  		begin
			`ovm_create(lli)
			lli.data = src[i][j];
			lli.op = WRITE;
			lli.addr = 5'b11111;
			lli.data_id = k;
			if(i*j==127*127)
				lli.last_data = 1;
			`ovm_send(lli)
			get_response(rsp);
		end
	end

	`ovm_create(lli)
	lli.op = READ;
	lli.addr = 5'b11110;
	lli.data_id = -100;
	`ovm_send(lli)
	get_response(rsp);
	`ovm_info("sysif receive", "Init(1)...", OVM_LOW)
	
	foreach(dst[i,j])
	begin
		`ovm_create(lli)
		lli.op = READ;
		lli.addr = 5'b11110;
		lli.data_id = k;
		if(k==1)
			lli.last_data = 1;
		k--;
		`ovm_send(lli)
		get_response(rsp);
		dst[height-1-i][width-1-j] = rsp.data;
	end

	fileId = $fopen(filename2, "w");
	foreach(dst[i,j])
	begin
		$write("%d", dst[i][j]);
		$fwrite(fileId, "%d", dst[i][j]);
		//$fdisplay(fileId, "%d", dst[i][j]);
		if(j==127)
		begin
			$write("\n");
			$fwrite(fileId, "\n");
			//$fdisplay(fileId, "\n");
		end
	end
	$fclose(fileId);
	
  endtask : body
endclass : Special_128X128_seq

class Special_256X256_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "Special_256X256_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(Special_256X256_seq, NewLabel_sysif_sequencer)

  tr_sysif lli;
  string filename = "./testImg/srcImage_256x256_30.txt";
  string filename2 = "./testImg/srcImage_256x256_30_result.txt";
  int width=256, height=256;
  logic[7:0] src[256][256];
  logic[23:0]	dst[256][256];
  //src = new[height][width];
  //dst = new[height][width];
  int i,j,k=0;
  integer fileId;
  
  virtual task body();
	void'(p_sequencer.get_config_string("Special_256X256_seq.filename", filename));
    void'(p_sequencer.get_config_string("Special_256X256_seq.filename2", filename2));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.width", width));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.height", height));

    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename), OVM_MEDIUM);
    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename2), OVM_MEDIUM);

    $readmemh(filename, src);

  	for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++, k++)
  		begin
			`ovm_create(lli)
			lli.data = src[i][j];
			lli.op = WRITE;
			lli.addr = 5'b11111;
			lli.data_id = k;
			if(i*j==(width-1)*(height-1))
				lli.last_data = 1;
			`ovm_send(lli)
			get_response(rsp);
		end
	end

	`ovm_create(lli)
	lli.op = READ;
	lli.addr = 5'b11110;
	lli.data_id = -100;
	`ovm_send(lli)
	get_response(rsp);
	`ovm_info("sysif receive", "Init(1)...", OVM_LOW)
	
	foreach(dst[i,j])
	begin
		`ovm_create(lli)
		lli.op = READ;
		lli.addr = 5'b11110;
		lli.data_id = k;
		if(k==1)
			lli.last_data = 1;
		k--;
		`ovm_send(lli)
		get_response(rsp);
		dst[height-1-i][width-1-j] = rsp.data;
	end

	fileId = $fopen(filename2, "w");
	foreach(dst[i,j])
	begin
		//$write("%d", dst[i][j]);
		$fwrite(fileId, "%d", dst[i][j]);
		//$fdisplay(fileId, "%d", dst[i][j]);
		if(j==(width-1))
		begin
			//$write("\n");
			$fwrite(fileId, "\n");
			//$fdisplay(fileId, "\n");
		end
	end
	$fclose(fileId);
	
  endtask : body
endclass : Special_256X256_seq

class Special_512X512_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "Special_512X512_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(Special_512X512_seq, NewLabel_sysif_sequencer)

  tr_sysif lli;
  string filename = "./testImg/srcImage_512x512_30.txt";
  string filename2 = "./testImg/srcImage_512x512_30_result.txt";
  int width=512, height=512;
  logic[7:0] src[512][512];
  logic[23:0]	dst[512][512];
  //src = new[height][width];
  //dst = new[height][width];
  int i,j,k=0;
  integer fileId;
  
  virtual task body();
	void'(p_sequencer.get_config_string("Special_512X512_seq.filename", filename));
    void'(p_sequencer.get_config_string("Special_512X512_seq.filename2", filename2));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.width", width));
    //void'(p_sequencer.get_config_int("Special_128X128_seq.height", height));

    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename), OVM_MEDIUM);
    p_sequencer.ovm_report_info(get_type_name(), 
    				$psprintf("%s starting... file name is %s", get_sequence_path(), filename2), OVM_MEDIUM);

    $readmemh(filename, src);

  	for(i=0; i<height; i++)
  	begin
  		for(j=0; j<width; j++, k++)
  		begin
			`ovm_create(lli)
			lli.data = src[i][j];
			lli.op = WRITE;
			lli.addr = 5'b11111;
			lli.data_id = k;
			if(i*j==(width-1)*(height-1))
				lli.last_data = 1;
			`ovm_send(lli)
			get_response(rsp);
		end
	end

	`ovm_create(lli)
	lli.op = READ;
	lli.addr = 5'b11110;
	lli.data_id = -100;
	`ovm_send(lli)
	get_response(rsp);
	`ovm_info("sysif receive", "Init(1)...", OVM_LOW)
	
	foreach(dst[i,j])
	begin
		`ovm_create(lli)
		lli.op = READ;
		lli.addr = 5'b11110;
		lli.data_id = k;
		if(k==1)
			lli.last_data = 1;
		k--;
		`ovm_send(lli)
		get_response(rsp);
		dst[height-1-i][width-1-j] = rsp.data;
	end

	fileId = $fopen(filename2, "w");
	foreach(dst[i,j])
	begin
		//$write("%d", dst[i][j]);
		$fwrite(fileId, "%d", dst[i][j]);
		//$fdisplay(fileId, "%d", dst[i][j]);
		if(j==(width-1))
		begin
			//$write("\n");
			$fwrite(fileId, "\n");
			//$fdisplay(fileId, "\n");
		end
	end
	$fclose(fileId);
	
  endtask : body
endclass : Special_512X512_seq

class work_seq extends NewLabel_sysif_base_sequence;
  function new (string name = "work_seq");
    super.new(name);
  endfunction : new
  `ovm_sequence_utils(work_seq, NewLabel_sysif_sequencer)

  sysif_config_seq cfg_seq;
  Special_16X16_seq img_seq16;
  Special_128X128_seq img_seq128;
  Special_256X256_seq img_seq256;
  Special_512X512_seq img_seq512;

  int unsigned seq_type = 16;

  task body();
  	`ovm_do(cfg_seq)
  	//if(seq_type == 16)
  	if(cfg_seq.img_width == 16)
  		`ovm_do(img_seq16)
  	//else if(seq_type == 128)
  	else if(cfg_seq.img_width == 128)
  		`ovm_do(img_seq128)
  	else if(cfg_seq.img_width == 256)
  		`ovm_do(img_seq256)
  	else if(cfg_seq.img_width == 512)
  		`ovm_do(img_seq512)
  	else
  		$stop();
  endtask
  
endclass

////////////////////////// AGENT /////////////////////////
class NewLabel_sysif_agent extends ovm_agent;
  ovm_active_passive_enum is_active = OVM_ACTIVE;
  
  `ovm_component_utils_begin(NewLabel_sysif_agent)
    `ovm_field_enum(ovm_active_passive_enum, is_active, OVM_ALL_ON)
  `ovm_component_utils_end
  
  NewLabel_sysif_sequencer sysif_seqr;
  NewLabel_sysif_driver sysif_driver;
  
  function new(string name, ovm_component parent=null);
    super.new(name, parent);
  endfunction
  
  virtual function void build();
    super.build();
    if (is_active == OVM_ACTIVE) 
    begin
      sysif_seqr = NewLabel_sysif_sequencer::type_id::create("sysif_seqr",this);
      sysif_driver = NewLabel_sysif_driver::type_id::create("sysif_driver",this);
    end
  endfunction : build
  
  virtual function void connect();
    super.connect();
    if(is_active == OVM_ACTIVE) 
    begin
      sysif_driver.seq_item_port.connect(sysif_seqr.seq_item_export);
    end
  endfunction : connect

  function void assign_vi(virtual interface SysIf bif);
  	if (is_active == OVM_ACTIVE)
  	begin
  		sysif_driver.assign_vi(bif);
  	end
  endfunction : assign_vi
endclass : NewLabel_sysif_agent


`endif // NewLabel_SYSIF_AGENT_SV
